/*
 * Точки сочленения
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>
#include <limits.h>

#ifdef DEBUG
# define debug_fprintf(x)	fprintf x
#else
# define debug_fprintf(x)	((void) 0)
#endif

#define MIN(a,b)	((a)<(b)?(a):(b))

/* {{{ memory allocation wrappers */
extern void *s_calloc(size_t n, size_t size)
{
	void *ptr;
	ptr = calloc(n, size);
	assert(ptr != NULL);
	if (ptr == NULL) {
		debug_fprintf((stderr, "failed to allocate %zu bytes\n"
					"out of memory\n", n * size));
	}
	return ptr;
}

extern void *s_malloc(size_t size)
{
	void *ptr;
	ptr = malloc(size);
	assert(ptr != NULL);
	if (ptr == NULL) {
		debug_fprintf((stderr, "failed to allocate %zu bytes\n"
					"out of memory\n", size));
	}
	return ptr;
}
/* }}} */

/* {{{ autoarray */
struct vector {
	int *data;
	int n;
	int max;
};
/* {{{ functions */
extern int
vector_init(struct vector *v, int p)
{
	if (v == NULL) {
		fprintf(stderr, "vector_init(): received NULL ptr, not doing anything\n");
		return 1;
	}

	if (p < 0 || p >= (CHAR_BIT * sizeof v->data[0]))
		p = 0;
	v->max = 1 << p;
	v->n = 0;
	v->data = (int *) s_calloc((size_t) v->max, sizeof *v->data);

	return 0;
}

extern int
vector_append(struct vector *v, int a)
{
	int *t = NULL;

	if (v == NULL) {
		fprintf(stderr, "vector_append(): received NULL ptr, not doing anything\n");
		return 1;
	}

	if (v->max == 0)
		vector_init(v, 0);

	if (v->n + 1 > v->max) {
		t = (int *) realloc(v->data, sizeof *t * (v->max *= 2));
		if (!t) {
			fprintf(stderr, "vector_append(): failed to allocate %d bytes\n", v->max);
			return 1;
		} else {
			v->data = t;
		}
	}

	v->data[v->n++] = a;

	return 0;
}

extern int
vector_sort(struct vector *v, int (*cmpf)(const void *, const void *))
{
	if (v == NULL) {
		fprintf(stderr, "vector_sort(): received NULL ptr, not doing anything\n");
		return 1;
	}

	if (v->max > 0 && v->n > 1) {
		qsort(v->data, (size_t) v->n, sizeof *v->data, cmpf);
		return 0;
	} else {
		return 1;
	}
}

extern int
vector_cleanup(struct vector *v)
{
	if (v == NULL) {
		fprintf(stderr, "vector_cleanup(): received NULL ptr, not doing anything\n");
		return 1;
	}

	if (v->max > 0) {
		v->max = v->n = 0;
		free(v->data);
		v->data = NULL;
		return 0;
	} else {
		return 1;
	}
}
/* }}} */
/* }}} */

/* {{{ doubly-linked list */
struct dll_node {
	int x;
	struct dll_node *prev, *next;
};
/* {{{ functions */
/*
 * This function inserts one node pointed to by 'ins' _after_ 'node'.
 */
extern struct dll_node *
dll_insert(struct dll_node *node, struct dll_node *ins)
{
	assert(node != NULL);
	if (ins == NULL) {
		debug_fprintf((stderr, "dll_insert(): ins is NULL, not doing anything\n"));
		return node;
	}

	if (node->next) {
		assert(node->next->prev == node);
		node->next->prev = ins;
	}
	ins->next = node->next;
	ins->prev = node;
	node->next = ins;
	return ins;
}

/*
 * This function extracts 'node' from a doubly-linked list and returns pointer
 * to that node, which pointers '->prev' and '->next' are not reset.
 *
 * If you really want to get rid of a node, call
 * 	free(dll_delete(unneeded_node));
 */
extern struct dll_node *
dll_delete(struct dll_node *node)
{
	if (!node) {
		debug_fprintf((stderr, "dll_delete(): received NULL ptr, wtf am i supposed to delete?\n"));
		return NULL;
	}

	if (node->prev) {
		assert(node->prev->next == node);
		node->prev->next = node->next;
	}
	if (node->next) {
		assert(node->next->prev == node);
		node->next->prev = node->prev;
	}

	return node;
}
/* }}} */
/* }}} */

/* {{{ graph repr: adjlist w/ doubly-linked list */
enum vertex_st {
	VTX_ST_UNKN = 0,
	VTX_ST_OPEN,
	VTX_ST_VISITED
};

struct adjlist_dll {
	struct dll_node *head;
	struct dll_node *tail;
	size_t _list_len;

	enum vertex_st state;
	short color;
	int distance;
	int prev;
	int low;
	int fwd, back;
	int dfs_ascnum; /* number of children in DFS tree */
};
/* {{{ functions */
extern void
adjl_dll_connect(struct adjlist_dll *node, const int conid)
{
	struct dll_node *Wilson;

	Wilson = (struct dll_node *) s_calloc(1, sizeof *Wilson);
	Wilson->x = conid;
	Wilson->prev = Wilson->next = NULL;

	if (node->head == NULL) {
		assert(node->tail == NULL);
		node->head = node->tail = Wilson;
		node->_list_len = 1;
	} else {
		dll_insert(node->tail, Wilson);
		node->_list_len++;
	}
}

extern void
adjl_dll_cleanup(struct adjlist_dll *al)
{
	struct dll_node *nx = al->head, *pr = al->head;

	while (nx) {
		pr = nx;
		nx = nx->next;
		free(pr);
	}
	al->_list_len = 0;
	al->head = al->tail = NULL;
}
/* }}} */
/* }}} */

int
numcmp(const void *p1, const void *p2)
{
	if (* (const int *) p1 < * (const int *) p2)
		return -1;
	else if (* (const int *) p1 == * (const int *) p2)
		return 0;
	else
		return 1;
}

int
dfs_find_cut_vtxs(struct adjlist_dll *al, const int s, const int anc,
		int *fwd_ct, struct vector *ap)
{
	struct dll_node *nd;
	bool is_cut_vtx = anc == -1 ? true : false;
	int ret = 0;

	if (al[s].state == VTX_ST_VISITED) {
		debug_fprintf((stderr, "%d\tthis node was already visited, called from node %d\n",
					s + 1, anc + 1));
		return 0;
	}

	debug_fprintf((stderr, "%d\tentered node from %d\n",
				s + 1, anc + 1));

	al[s].state = VTX_ST_OPEN;
	al[s].prev = anc;
	al[s].low = al[s].fwd = ++*fwd_ct;

	for (nd = al[s].head; nd != NULL; nd = nd->next) {
		if (nd->x == anc || nd->x == s)
			continue;

		debug_fprintf((stderr, "%d\tlooking at edge (%d, %d)\n",
					s + 1, s + 1, nd->x + 1));

		if (al[nd->x].fwd == 0) {
			al[s].dfs_ascnum++;
			ret |= dfs_find_cut_vtxs(al, nd->x, s, fwd_ct, ap);

			debug_fprintf((stderr, "%d\tal[%d].low(%d) = ",
						s + 1, s + 1, al[s].low));
			al[s].low = MIN(al[s].low, al[nd->x].low);
			debug_fprintf((stderr, "%d\n",
						al[s].low));

			if (is_cut_vtx == false && al[nd->x].low >= al[s].fwd) {
				debug_fprintf((stderr, "%d\tnode is articulation point "
							"(al[%d].low=%d >= al[%d].fwd=%d)\n",
							s + 1, nd->x + 1, al[nd->x].low, s + 1, al[s].fwd));
				vector_append(ap, s);
				is_cut_vtx = true;
			}
		} else {
			if (al[nd->x].prev == -1) {
				debug_fprintf((stderr, "%d\tfound back edge to the tree root\n", s + 1));
				ret |= 1;
			}

			debug_fprintf((stderr, "%d\tal[%d].low(%d) = ",
						s + 1, s + 1, al[s].low));
			al[s].low = MIN(al[s].low, al[nd->x].fwd); /* whoops */
			debug_fprintf((stderr, "%d\n",
						al[s].low));
		}
	}

	al[s].state = VTX_ST_VISITED;

	if (anc == -1 && al[s].dfs_ascnum >= 2) {
		debug_fprintf((stderr, "%d\tdfs tree root is articulation point\n",
					s + 1));
		vector_append(ap, s);
	}

	debug_fprintf((stderr, "%d\treturn %d\n", s + 1, ret));
	return ret;
}

int
main(int argc, char **argv)
{
	struct adjlist_dll *al;
	int i, n, m, a, b;
	struct vector ap; /* articulation point (aka cut vertex) */
	int fwd_ct = 0;

	scanf("%d%d", &n, &m);
	al = (adjlist_dll *) calloc(n, sizeof *al);
	vector_init(&ap, 6);
	for (i = 0; i < m; i++) {
		scanf("%d%d", &a, &b);
		--a, --b;
		adjl_dll_connect(al + a, b);
		adjl_dll_connect(al + b, a);
	}

	for (i = 0; i < n; i++) {
		if (al[i].state == VTX_ST_UNKN) {
			dfs_find_cut_vtxs(al, i, -1, &fwd_ct, &ap);
		}
	}

	vector_sort(&ap, numcmp);
	printf("%d\n", ap.n);
	for (i = 0; i < ap.n; i++)
		printf("%d ", ap.data[i] + 1);
	fputc('\n', stdout);

	for (i = 0; i < n; i++) {
		debug_fprintf((stderr, "node %3d { _list_len=%3zu, prev=%3d, fwd=%3d, low=%3d }\n",
					i + 1, al[i]._list_len, al[i].prev + 1,
					al[i].fwd, al[i].low));
		adjl_dll_cleanup(al + i);
	}
	free(al);
	vector_cleanup(&ap);
	return 0;
}
